class JsNativeBridge {

    /**
       * 
       * @param className 
       * @param methodName 
       * @param parameters 
       * @param methodSignature 
       */
    static callCocosStaticMethod(className, methodName, parameters, methodSignature = "(Ljava/lang/String;)V") {
        let cc: any = window['cc']
        let jsb: any = window['jsb']
        cc.log("NativeTest JsNativeBridge callStaticMethod ", cc.sys.isNative)
        if (!cc.sys.isNative) {
            return -1;
        }
        cc.log("NativeTest JsNativeBridge callStaticMethod ", methodName, parameters)
        if (cc.sys.os == cc.sys.OS_IOS) {
            let result = jsb.reflection.callStaticMethod(className, methodName, parameters);
            if (result) {
                cc.log("NativeTest callStaticMethod ios  ", result);
            }
        } else {
            cc.log("NativeTest JsNativeBridge callStaticMethod adroid ")
            let result = jsb.reflection.callStaticMethod(className, methodName, methodSignature, parameters)
            cc.log("NativeTest callStaticMethod result  ", result);
            if (result) {

                return result;
            }
        }
    }
    /**
  * 
  * @param className 
  * @param methodName 
  * @param parameters 
  * @param callback 
  */

    static callLayaStaticMethod(className: string, methodName: string, parameters: any, callback?: Function) {
        var os: any = window['conchConfig'].getOS();
        var bridge: any;
        let PlatformClass: any = window['PlatformClass']
        if (os == "Conch-ios") {
            bridge = PlatformClass.createClass(className);//创建脚步代理
            if (!bridge) {
                console.log(' callStatic Method class name ', className, methodName, parameters)
                return;
            }
            if (callback) {
                bridge.callWithBack(function (value) {
                    var obj = JSON.parse(value)
                    console.log(obj);
                    callback(obj)
                }, methodName + ":", JSON.stringify(parameters));

            } else {
                bridge.call(methodName, parameters)

            }

        }
        else if (os == "Conch-android") {
            //需要完整的类路径，注意与iOS的不同
            bridge = PlatformClass.createClass(className);//创建脚步代理
            if (!bridge) {
                console.log(' callStatic Method class name ', className, methodName, parameters)
                return;
            }
            if (callback) {
                bridge.callWithBack(function (value) {
                    var obj = JSON.parse(value)
                    console.log(obj);
                    callback(obj)
                }, methodName, JSON.stringify(parameters));
            } else {
                bridge.call(methodName, parameters)
            }

        }



    }

    /**
     * 如果使用laya引擎替换为LayaJsNativeBridge使用
     * @param className java 类名
     * @param methodName java 方法名
     * @param parameters json格式的参数
     * @param methodSignOrFunc 函数声明或者回调函数
     */
    static callStaticMethod(className: string, methodName: string, parameters: string, methodSignOrFunc?: any) {
        if (window['cc']) {
            console.log('NativeTest cocos creator ')
            this.callCocosStaticMethod(className, methodName, parameters, methodSignOrFunc)
        } else if (window['laya']) {
            console.log(' laya ')
            this.callLayaStaticMethod(className, methodName, parameters, methodSignOrFunc)
        }

    }


}

class SdkObjectSubject<T = any> {
    protected list: T[] = []

    add(v: T): void {
        this.list.push(v)
    }

    remove(v: T) {
        let index = this.list.indexOf(v)
        if (index >= 0) {
            this.list.splice(index, 1)
        }
    }

    getList() {
        return this.list;
    }
}

class SdkEventList {
    protected loadListener: Function[] = []
    on(func: Function) {
        let index = this.loadListener.indexOf(func)
        if (index < 0) {
            this.loadListener.push(func)
        }
    }
    off(func: Function) {
        let index = this.loadListener.indexOf(func)
        if (index >= 0) {
            this.loadListener.splice(index, 1)
        }
    }

    emit(data: any) {
        for (let index = 0; index < this.loadListener.length; index++) {
            const element = this.loadListener[index];
            element(data)
        }
    }
}
const NativeName = {
    RENDERAD_CLASS_NAME: 'sdk/bridge/SelfRenderAdBridge',
    TEMPLATE_CLASS_NAME: 'sdk/bridge/TemplateAdBridge',
    BANNER_CLASS_NAME: 'sdk/bridge/BannerAdBridge',
    INSERTAD_CLASS_NAME: 'sdk/bridge/InsertAdBridge',
    REWARDAD_CLASS_NAME: 'sdk/bridge/RewardAdBridge',
    CLASS_NAME: 'sdk/bridge/SystemBridge',
    NATIVEAD_CLASS_NAME: '',
    create: 'create',
    load: 'load',
    show: 'show',
    destroy: 'destroy',
    hide: 'hide',
    login: 'login',
    reportAdClick: 'reportAdClick',
    reportAdShow: 'reportAdShow',
    exitApp: "exitApp",
    initAd: "initAd",
}
export class NativeAd {

    public adUnitId: string = ''
    public data: any = {}
    protected loadListener: SdkEventList = new SdkEventList()
    protected errorListener: SdkEventList = new SdkEventList()

    protected subject: SdkObjectSubject;

    constructor(subject: SdkObjectSubject) {
        this.subject = subject;
        this.subject.add(this)
    }

    removeFromSubject() {
        this.subject.remove(this)
    }
    getAdUnitId() {
        return this.data['adUnitId'];
    }


    onLoad(func: Function) {
        this.loadListener.on(func)
    }

    offLoad(func: Function) {
        this.loadListener.off(func)
    }

    emitLoad(data) {
        this.loadListener.emit(data)
    }

    onError(func: Function) {
        this.errorListener.on(func)

    }

    offError(func: Function) {
        this.errorListener.off(func)
    }

    emitError(data) {
        this.errorListener.emit(data)
    }
}


export class NativeBannerAd extends NativeAd {

    protected resizeListener: SdkEventList = new SdkEventList()

    layout: number = 0;
    width: number = 0;
    height: number = 0;

    constructor(subject: SdkObjectSubject) {
        super(subject);
    }

    bannerResize(ad: string, size) {
        if (ad == this.getAdUnitId())
            this.emitLoad(size)
    }

    bannerAdCallback(ad: string, result: number) {
        console.log('NativeTest bannerAdCallback result ', result)
        if (ad == this.getAdUnitId()) {
            if (result == 1) {
                this.emitLoad(null)
            } else {
                this.emitError('加载失败')
            }
        }

    }

    create(data: any) {
        JsNativeBridge.callStaticMethod(NativeName.BANNER_CLASS_NAME, NativeName.create, JSON.stringify(data))
    }

    load() {
        JsNativeBridge.callStaticMethod(NativeName.BANNER_CLASS_NAME, NativeName.load, this.getAdUnitId())
    }

    hide() {
        JsNativeBridge.callStaticMethod(NativeName.BANNER_CLASS_NAME, NativeName.hide, this.getAdUnitId())
    }

    show() {
        JsNativeBridge.callStaticMethod(NativeName.BANNER_CLASS_NAME, NativeName.show, this.getAdUnitId())
    }

    destroy() {
        this.removeFromSubject()
        JsNativeBridge.callStaticMethod(NativeName.BANNER_CLASS_NAME, NativeName.destroy, this.getAdUnitId())

    }

    onResize(func: Function) {
        this.resizeListener.on(func)
    }

    offResize(func: Function) {
        this.resizeListener.off(func)
    }


    emitResize(data) {
        this.resizeListener.emit(data)
    }


}


export class NativeInterstitialAd extends NativeAd {



    insertAdCallback(id: string, result: number) {
        if (id == this.getAdUnitId()) {
            if (result == 1) {
                this.emitLoad(null)
            } else {
                this.emitError('加载失败')
            }
        }
    }
    create(data) {
        JsNativeBridge.callStaticMethod(NativeName.INSERTAD_CLASS_NAME, NativeName.create, JSON.stringify(data))
    }

    show() {
        JsNativeBridge.callStaticMethod(NativeName.INSERTAD_CLASS_NAME, NativeName.show, this.getAdUnitId())
    }

    load() {
        JsNativeBridge.callStaticMethod(NativeName.INSERTAD_CLASS_NAME, NativeName.load, this.getAdUnitId())
    }

    destroy() {
        this.removeFromSubject()
        JsNativeBridge.callStaticMethod(NativeName.INSERTAD_CLASS_NAME, NativeName.destroy, this.getAdUnitId())
    }

}


export class NativeNativeAd extends NativeAd {


    nativeAdCallback(id: string, result: number) {
        if (id == this.getAdUnitId()) {
            if (result == 1) {
                this.emitLoad(null)
            } else {
                this.emitError('加载失败')
            }
        }
    }
    create(data) {
        JsNativeBridge.callStaticMethod(NativeName.NATIVEAD_CLASS_NAME, NativeName.create, JSON.stringify(data))
    }

    reportAdShow(data) {
        JsNativeBridge.callStaticMethod(NativeName.NATIVEAD_CLASS_NAME, NativeName.reportAdShow, JSON.stringify(data))
    }

    reportAdClick(data) {
        JsNativeBridge.callStaticMethod(NativeName.NATIVEAD_CLASS_NAME, NativeName.reportAdClick, JSON.stringify(data))
    }

    load() {
        JsNativeBridge.callStaticMethod(NativeName.NATIVEAD_CLASS_NAME, NativeName.load, this.getAdUnitId())
    }

    destroy() {
        this.removeFromSubject()
        JsNativeBridge.callStaticMethod(NativeName.NATIVEAD_CLASS_NAME, NativeName.destroy, this.getAdUnitId())
    }
}


export class NativeRewardedVideoAd extends NativeAd {

    protected closeListener: SdkEventList = new SdkEventList()


    create(data) {
        JsNativeBridge.callStaticMethod(NativeName.REWARDAD_CLASS_NAME, NativeName.create, JSON.stringify(data))
    }


    load() {
        JsNativeBridge.callStaticMethod(NativeName.REWARDAD_CLASS_NAME, NativeName.load, this.getAdUnitId())
    }

    show() {
        JsNativeBridge.callStaticMethod(NativeName.REWARDAD_CLASS_NAME, NativeName.show, this.getAdUnitId())
    }

    destroy() {
        this.removeFromSubject()
        JsNativeBridge.callStaticMethod(NativeName.REWARDAD_CLASS_NAME, NativeName.destroy, this.getAdUnitId())
    }

    onClose(func: Function) {
        this.closeListener.on(func)
    }
    offClose(func: Function) {
        this.closeListener.off(func)
    }

    emitClose(data) {
        this.closeListener.emit(data)
    }

    rewardAdCallback(ad: string, result: number) {
        if (ad == this.getAdUnitId()) {
            console.log(' rewardAdCallback result ', result)
            if (result == 1) {
                this.emitLoad(null)
            } else {
                this.emitError('加载失败')
            }
        }

    }


    rewardAdClose(ad: string, result: number) {
        if (ad == this.getAdUnitId()) {
            this.emitClose({ isEnded: result })
        }

    }
}
export class NativeSelftRenderAd extends NativeAd {

    selfRenderAdCallback(id: string, result: number) {
        if (id == this.getAdUnitId()) {
            if (result == 1) {
                this.emitLoad(null)
            } else {
                this.emitError('加载失败')
            }
        }
    }
    create(data) {
        JsNativeBridge.callStaticMethod(NativeName.RENDERAD_CLASS_NAME, NativeName.create, JSON.stringify(data))
    }

    load() {
        JsNativeBridge.callStaticMethod(NativeName.RENDERAD_CLASS_NAME, NativeName.load, this.getAdUnitId())
    }

    show() {
        JsNativeBridge.callStaticMethod(NativeName.RENDERAD_CLASS_NAME, NativeName.show, this.getAdUnitId())
    }

    destroy() {
        this.removeFromSubject()
        JsNativeBridge.callStaticMethod(NativeName.RENDERAD_CLASS_NAME, NativeName.destroy, this.getAdUnitId())
    }
}


export class NativeTemplateAd extends NativeAd {


    templateAdCallback(id: string, result: number) {
        if (id == this.getAdUnitId()) {
            if (result == 1) {
                this.emitLoad(null)
            } else {
                this.emitError('加载失败')
            }
        }
    }
    create(data) {
        JsNativeBridge.callStaticMethod(NativeName.TEMPLATE_CLASS_NAME, NativeName.create, JSON.stringify(data))
    }

    show() {
        JsNativeBridge.callStaticMethod(NativeName.TEMPLATE_CLASS_NAME, NativeName.show, this.getAdUnitId())
    }

    load() {
        JsNativeBridge.callStaticMethod(NativeName.TEMPLATE_CLASS_NAME, NativeName.load, this.getAdUnitId())
    }

    destroy() {
        this.removeFromSubject()
        JsNativeBridge.callStaticMethod(NativeName.TEMPLATE_CLASS_NAME, NativeName.destroy, this.getAdUnitId())
    }

}

class NativeSystem {

    private static loginSuccess: Function = null;
    private static loginFail: Function = null;
    static login(param: any) {
        this.loginSuccess = param["success"];
        this.loginFail = param["fail"]
        JsNativeBridge.callStaticMethod(NativeName.CLASS_NAME, NativeName.login, JSON.stringify(param))
    }

    static loginCallback(param: any) {
        let list = param.split("$$");
        let data = list[0]
        let result = parseInt(list[1])
        if (result == 1) {
            if (this.loginSuccess) {
                this.loginSuccess(data)
            }
        } else {
            if (this.loginFail) {
                this.loginFail(data)
            }
        }
    }

    static exitApp(param: any) {
        JsNativeBridge.callStaticMethod(NativeName.CLASS_NAME, NativeName.exitApp, "")
    }

    static initAd() {
        JsNativeBridge.callStaticMethod(NativeName.CLASS_NAME, NativeName.initAd, "")
    }
}
class NativeAgreement {
    private static loginSuccess: Function = null;
    private static loginFail: Function = null;
    static showUserAgreement(param: any) {
        this.loginSuccess = param["success"];
        this.loginFail = param["fail"]
        JsNativeBridge.callStaticMethod(NativeName.CLASS_NAME, "showAgreement", JSON.stringify(param))
    }

    static userAgreementCallback(result: any) {
        console.log("NativeTest JsNativeBridge userAgreementCallback result ", result)
        // let list = param.split("$$");
        // let data = list[0]
        // let result = parseInt(list[1])
        if (result == 1) {
            if (this.loginSuccess) {
                this.loginSuccess()
            }
        } else {
            if (this.loginFail) {
                this.loginFail()
            }
        }
    }
}
let $$ = '$$'
export default class Native {


    protected static rewardAdSubject: SdkObjectSubject<NativeRewardedVideoAd> = new SdkObjectSubject()
    protected static selftRenderAdSubject: SdkObjectSubject<NativeSelftRenderAd> = new SdkObjectSubject()
    protected static nativeAdSubject: SdkObjectSubject<NativeNativeAd> = new SdkObjectSubject()
    protected static bannerAdSubject: SdkObjectSubject<NativeBannerAd> = new SdkObjectSubject()
    protected static insertAdSubject: SdkObjectSubject<NativeInterstitialAd> = new SdkObjectSubject()
    protected static templateAdSubject: SdkObjectSubject<NativeTemplateAd> = new SdkObjectSubject()

    static createSelfRenderAd(data) {
        let ad = new NativeSelftRenderAd(this.selftRenderAdSubject);
        ad.data = data;
        ad.create(data)
        return ad;
    }

    static createNativeAd(data): NativeNativeAd {
        let ad = new NativeNativeAd(this.nativeAdSubject);
        ad.data = data;
        ad.create(data)
        return ad;
    }

    static createBannerAd(data) {
        let ad = new NativeBannerAd(this.bannerAdSubject);
        ad.data = data;
        ad.create(data)
        return ad;
    }

    static createRewardedVideoAd(data) {
        console.log('NativeTest createRewardedVideoAd data ', data)
        let ad = new NativeRewardedVideoAd(this.rewardAdSubject);
        ad.data = data;
        ad.create(data)
        return ad;
    }

    static createInterstitialAd(data) {
        let ad = new NativeInterstitialAd(this.insertAdSubject)
        ad.data = data;
        ad.create(data)
        return ad;
    }
    static createTemplateAd(data) {
        let ad = new NativeTemplateAd(this.templateAdSubject)
        ad.data = data;
        ad.create(data)
        return ad;
    }
    static getSystemInfoSync() {
        return {
            width: 300,
            height: 200,
        }
    }

    //java call functions 
    static bannerAdCallback(param: string) {
        console.log('NativeTest bannerAdCallback param', param)
        let list = param.split($$);
        let id = list[0]
        let result = parseInt(list[1])
        let observers = this.bannerAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.bannerAdCallback(id, result)
        }
    }


    static bannerResize(param: string) {
        console.log('NativeTest bannerResize param', param)
        let list = param.split($$);
        let id = list[0]
        let size = list[1]
        let observers = this.bannerAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.bannerResize(id, size)
        }
    }



    static insertAdCallback(param: string) {
        let list = param.split($$);
        let id = list[0]
        let result = parseInt(list[1])
        let observers = this.insertAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.insertAdCallback(id, result)
        }
    }

    static rewardAdCallback(param: string) {
        console.log('NativeTest rewardAdCallback param', param)
        let list = param.split($$);
        let id = list[0]
        let result = parseInt(list[1])
        console.log('NativeTest rewardAdCallback result ', id, result)
        let observers = this.rewardAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.rewardAdCallback(id, result)
        }
    }


    static rewardAdClose(param: string) {
        console.log('NativeTest rewardAdClose param', param)
        let list = param.split($$);
        let id = list[0]
        let result = parseInt(list[1])
        console.log('NativeTest rewardAdClose result ', id, result)
        let observers = this.rewardAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.rewardAdClose(id, result)
        }
    }

    static templateAdCallback(param: string) {
        console.log('NativeTest templateAdCallback param', param)
        let list = param.split($$);
        let id = list[0]
        let result = parseInt(list[1])
        console.log('NativeTest templateAdCallback result ', id, result)
        let observers = this.templateAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.templateAdCallback(id, result)
        }
    }
    static selfRenderAdCallback(param: string) {
        console.log('NativeTest selfRenderAdCallback param', param)
        let list = param.split($$);
        let id = list[0]
        let result = parseInt(list[1])
        console.log('NativeTest selfRenderAdCallback result ', id, result)
        let observers = this.selftRenderAdSubject.getList();
        for (let index = 0; index < observers.length; index++) {
            const element = observers[index];
            element.selfRenderAdCallback(id, result)
        }
    }

    static login(param: any) {
        NativeSystem.login(param)
    }

    static loginCallback(data: any) {
        NativeSystem.loginCallback(data)
    }

    static showUserAgreement(param: any) {
        NativeAgreement.showUserAgreement(param)
    }

    static userAgreementCallback(result: number) {
        NativeAgreement.userAgreementCallback(result)
    }

    static exitApplication(param: any) {
        NativeSystem.exitApp(param)
    }

    static exitMiniProgram(param: any) {
        NativeSystem.exitApp(param)
    }

    static initAd() {
        NativeSystem.initAd()
    }
}
window['native'] = Native
